package com.qmock.pay.security;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.crypto.Cipher;

import com.qmock.pay.exception.CertInitializeException;
import com.qmock.pay.exception.SignEncException;
import org.apache.commons.io.IOUtils;
import org.apache.log4j.Logger;

/**
 * RSA 签名、验签
 * 
 * @author wenlong.du
 * 
 */
public class UmpayRsaHelper {

	private static Logger		logger	= Logger.getLogger(UmpayRsaHelper.class);

	private Cipher					cipher	= null;
	private Signature				sig			= null;
	private X509Certificate	cert		= null;

	/**
	 * 私有化构造函数
	 */
	private UmpayRsaHelper() {}

	/**
	 * 获得此帮助类的一个实体
	 * 
	 * @param rsaCrtPath CRT文件路径
	 * @param signP8Path 密钥文件路径
	 * @return
	 * @throws CertInitializeException
	 */
	public static UmpayRsaHelper getInstance(String rsaCrtPath, String signP8Path) throws CertInitializeException {
		UmpayRsaHelper umpHelper = new UmpayRsaHelper();
		umpHelper.init(rsaCrtPath, signP8Path);
		return umpHelper;

	}

	/**
	 * 加载证书
	 * 
	 * @param rsaCrtPath
	 * @param signP8Path
	 * @throws CertInitializeException
	 */
	private void init(String rsaCrtPath, String signP8Path) throws CertInitializeException {
		byte[] certFile = new byte[20480];

		InputStream in_cert = null;
		InputStream in_p8 = null;
		try {
			// 加载.crt证书////////////////////////
			in_cert = new FileInputStream(new File(rsaCrtPath));
			ByteArrayInputStream bais = new ByteArrayInputStream(certFile);
			in_cert.read(certFile);
			CertificateFactory cf = CertificateFactory.getInstance("X.509");
			cert = (X509Certificate) cf.generateCertificate(bais);
			byte[] keyBytes = cert.getPublicKey().getEncoded();

			X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
			KeyFactory keyFactory = KeyFactory.getInstance("RSA");
			PublicKey publicKey = keyFactory.generatePublic(x509KeySpec);

			cipher = Cipher.getInstance(keyFactory.getAlgorithm());
			cipher.init(Cipher.ENCRYPT_MODE, publicKey);

			// 加载.p8证书////////////////////////
			logger.info("加载P8证书");
			in_p8 = new FileInputStream(new File(signP8Path));
			byte[] b = new byte[20480];
			in_p8.read(b);
			PKCS8EncodedKeySpec peks = new PKCS8EncodedKeySpec(b);
			KeyFactory kf = KeyFactory.getInstance("RSA");
			PrivateKey pk = kf.generatePrivate(peks);
			sig = Signature.getInstance("SHA1withRSA");
			sig.initSign(pk);

		} catch (FileNotFoundException ex) {
			logger.error("MonitorClass=UmpRsaHelper.init    |   unionmobilepay  CRT证书文件找不到", ex);
			throw new CertInitializeException("CRT证书文件找不到");
		} catch (Exception ex) {
			logger.error("MonitorClass=UmpRsaHelper.init    |   unionmobilepay  CRT 初始化失败", ex);
			throw new CertInitializeException("unionmobilepay  CERT 初始化失败");
		} finally {
			IOUtils.closeQuietly(in_cert);
			IOUtils.closeQuietly(in_p8);
		}
	}

	/**
	 * 加密数据 对联动优势 卡号、信用卡有效期、信用卡CVN2/CVV2、交易密码、证件类型、证件号、持卡人姓名 进行RSA加密
	 * 
	 * @param value 加密源数据
	 * @return
	 * @throws CertInitializeException
	 */
	public String encrypt(String value) throws SignEncException {
		try {
			return Base64.encode(cipher.doFinal(value.getBytes("utf-8"))).replace("\n", "");
			//return Base64.encodeBase64String(cipher.doFinal(value.getBytes("GBK"))).replace("\n", ""); 
		} catch (Exception ex) {
			logger.error("MonitorClass=UmpRsaHelper.encrypt    |   unionmobilepay  CRT初始化失败", ex);
			throw new SignEncException("CRT初始化失败");
		}
	}

	/**
	 * 生成签名
	 * 
	 * @param plain 签名数据源
	 * @return 签名字符串
	 * @throws SignEncException
	 */
	public String sign(String plain) throws SignEncException {
		try {
			sig.update(plain.getBytes("utf-8")); 
			byte signData[] = sig.sign();
			 String sign = new String(Base64.encode(signData)).replace("\n", "");
			//String sign = new String(Base64.encodeBase64(signData)).replace("\n", "");
			return sign;
		} catch (Exception ex) {
			logger.error("MonitorClass=UmpRsaHelper.sign    |   unionmobilepay  签名异常", ex);
			throw new SignEncException("生成签名失败！");
		}
	}

	/**
	 * 验证签名
	 * 
	 * @param content 签名数据源
	 * @param sign 签名密文串
	 * @return boolean，为校验结果，true为验证通过，false为不通过
	 * @throws SignEncException
	 */
	public boolean verify(String content, String sign) throws SignEncException {
		try {
			byte[] signed = Base64.decode(sign);
			 //byte[] signed = Base64.decodeBase64(sign);
			Signature signature = Signature.getInstance("SHA1WithRSA");
			signature.initVerify(cert);
			signature.update(content.getBytes("GBK"));// 测试中发现对方数据做签名是用的GBK编码
			return signature.verify(signed);
		} catch (Exception ex) {
			logger.error("MonitorClass=UmpRsaHelper.verify    |   unionmobilepay  验证签名异常", ex);
			throw new SignEncException("验证签名失败！");
		}
	}

	/**
	 * 将加密后的数据URL编码，加密后调用 URL encode
	 */
	public void paramEncode(Map<String, String> param) throws UnsupportedEncodingException {
		Set<String> set = param.keySet();
		try {
			for (Iterator<String> it = set.iterator(); it.hasNext();) {
				String key = it.next();
				String value = param.get(key);
				if ("card_id".equals(key) || "valid_date".equals(key) || "cvv2".equals(key) || "sign".equals(key) || "identity_type".equals(key) || "identity_code".equals(key) || "card_holder".equals(key) || "pass_wd".equals(key)) {
					value = URLEncoder.encode(value, "utf-8");
					param.put(key, value);
				}
			}
		} catch (UnsupportedEncodingException ex) {
			logger.error("MonitorClass=UmpRsaHelper.paramEncode    |   unionmobilepay  URL encode异常", ex);
			throw new UnsupportedEncodingException("不支持的编码格式");
		}
	}

	/**
	 * 将加密后的数据URL解码
	 */
	public void paramDecode(Map<String, String> param) throws UnsupportedEncodingException {
		Set<String> set = param.keySet();
		try {
			for (Iterator<String> it = set.iterator(); it.hasNext();) {
				String key = it.next();
				String value = param.get(key);
				if ("card_id".equals(key) || "valid_date".equals(key) || "cvv2".equals(key) || "sign".equals(key) || "identity_type".equals(key) || "identity_code".equals(key) || "card_holder".equals(key) || "pass_wd".equals(key)) {
					value = URLDecoder.decode(value, "utf-8");
					param.put(key, value);
				}
			}
		} catch (UnsupportedEncodingException ex) {
			logger.error("MonitorClass=UmpRsaHelper.paramDecode    |   unionmobilepay  URL decode异常", ex);
			throw new UnsupportedEncodingException("不支持的编码格式");
		}
	}

	/**
	 * 对关键数据加密并完成签名
	 * @param map
	 * @return
	 */
	public String encryptAndSign(Map<String, String> map) {
		try {
			Set<String> set = map.keySet();
			for (Iterator<String> it = set.iterator(); it.hasNext();) {
				String key = it.next();
				String value = map.get(key);
				// 将关键数据加密
				if ("card_id".equals(key) || "valid_date".equals(key) || "cvv2".equals(key)
						|| "identity_type".equals(key) || "identity_code".equals(key) || "card_holder".equals(key)
						|| "pass_wd".equals(key)) {
					value = encrypt(value);
					map.put(key, value);
				}
			}
			return sign(getSignData(map));
		} catch (Exception e) {
			logger.error("加密验签异常", e);
		}
		return null;
	}

	/**
	 * 将Map转换为待签名或者验签的字符串表示，以&连接
	 * 
	 * @param params
	 * @return
	 */
	public static String getSignData(Map<String, String> params) {
		List<String> keys = new ArrayList<String>(params.keySet());
		Collections.sort(keys);
		StringBuilder signSource = new StringBuilder(64);
		for (int i = 0; i < keys.size(); i++) {
			String key = (String) keys.get(i);
			if ("sign".equals(key) || "sign_type".equals(key)) {// 做签名不需要这两个字段
				continue;
			}
			String value = params.get(key);
			if (i > 0) {
				signSource.append("&").append(key).append("=").append(value);
			} else {
				signSource.append(key).append("=").append(value);
			}
		}
		return signSource.toString();
	}

	/**
	 * 解析联动优势的响应
	 * 
	 * @param metaStr
	 * @return
	 */
	public static Map<String, String> parseMETA(String metaStr) {
		Map<String, String> metaMap = new HashMap<String, String>();
		if (metaStr == null) return metaMap;
		String[] kvs = metaStr.split("&");
		for (String str : kvs) {
			if (str.startsWith("sign")) {
				metaMap.put("sign", str.substring(str.indexOf("=") + 1));
				continue;
			}
			/*if (str.startsWith("ret_msg")) {
				metaMap.put("ret_msg", str.substring(str.indexOf("=") + 1));
				continue;
			}*/
			String[] kv = str.split("=");
			if (kv == null) continue;
			if (kv.length == 1) {
				metaMap.put(kv[0], "");
				continue;
			}
			metaMap.put(kv[0], kv[1]);
		}
		return metaMap;
	}
	
	/**
	 * 用于联动优势
	 * @param metaStr
	 * @return
	 */
	public static Map<String,String> parseMETAForPreumpay(String metaStr){
		Map<String,String> metaMap = new HashMap<String,String>();
		if(metaStr == null)return metaMap;
		String[] kvs = metaStr.split("&");
		for(String str : kvs){
			if(str.startsWith("sign")){
				metaMap.put("sign", str.substring(str.indexOf("=")+1));
				continue;
			}
			String[] kv = str.split("=");
			if(kv == null)continue;
			if(kv.length ==1){
				metaMap.put(kv[0],"");
				continue;
			}
			if(kv.length > 2){
				int index = str.indexOf("=");
				String value = str.substring(index+1, str.length());
				metaMap.put(kv[0],value);
				continue;
			}
			//if(kv.length != 2)continue;
			metaMap.put(kv[0], kv[1]);
		}
		return metaMap;
	}

	/**
	 * 将RecieveRequestDto 中的body map 转为简单的key-value形式
	 * 
	 * @param requestMap
	 * @return
	 */
	public static Map<String, String> getRecieveRequestMap(Map<String, String[]> requestMap) {
		Map<String, String> resMap = new HashMap<String, String>();
		for (Map.Entry<String, String[]> entry : requestMap.entrySet()) {
			String key = entry.getKey();
			String value = entry.getValue()[0];
			resMap.put(key, value);
		}
		return resMap;
	}

	/**
	 * 创建请求的URL
	 * 
	 * @param url
	 * @param map
	 * @return
	 */
	public static String createUrl(String url, Map<String, String> map) {
		boolean flag = false;
		StringBuilder sbuf = new StringBuilder(128);
		sbuf.append(url);
		Set<String> set = map.keySet();
		for (Iterator<String> it = set.iterator(); it.hasNext(); flag = true) {
			String key = it.next();
			String value = map.get(key);
			if (!flag) {
				sbuf.append("?").append(key).append("=").append(value);
			} else {
				sbuf.append("&").append(key).append("=").append(value);
			}
		}
		return sbuf.toString();
	}

	/**
	 * 将Map转为为字符串
	 * 
	 * @param map
	 * @return
	 */
	public static String packMap(Map<String, String> map) {
		StringBuilder sbuf = new StringBuilder(128);
		Set<String> set = map.keySet();
		boolean flag = false;
		for (Iterator<String> it = set.iterator(); it.hasNext(); flag = true) {
			String key = it.next();
			String value = map.get(key);
			if (!flag) {
				sbuf.append(key).append("=").append(value);
			} else {
				sbuf.append("&").append(key).append("=").append(value);
			}
		}
		return sbuf.toString();
	}

}
